# -*- coding: utf-8 -*-
import aiosqlite

from typing   import get_args
from datetime import datetime

from src       import utils
from src.types import Region, Weather, WeatherForecast, WEATHER_CONDITION

from fastapi           import Request, APIRouter, HTTPException
from fastapi.responses import Response, JSONResponse


region_weather = APIRouter(prefix = "/weather")


@region_weather.get("/{regionld}")
async def region_weather_get(request: Request, regionld: int):
    if regionld == None or 0 >= regionld:
        raise HTTPException(400, detail = "Invalid ID")

    weather = await Weather.get_by(request.app.database, id = regionld)

    if not weather:
        raise HTTPException(404, detail = "The region type with this ID was not found")
    
    if weather.weatherForecast == []:
        raise HTTPException(404, "Forecastid was not found in the region values")
    return JSONResponse(weather("id", "regionName", "temperature", "humidity", "windSpeed", "weatherCondition", "precipitationAmount", "measurementDateTime", "weatherForecast"), status_code = 200)


@region_weather.get("/search")
async def region_weather_search(request: Request):
    keys = ("id", "regionName", "temperature", "humidity", "windSpeed", "weatherCondition", "precipitationAmount", "measurementDateTime", "weatherForecast")
    sql  = f"SELECT {', '.join(keys)} FROM weather WHERE "

    params  = request.query_params
    filters = []

    start, end = None, None

    if (startDateTime := params.get("startDateTime")) != None:
        try:
            start = datetime.isoformat(startDateTime)
        except:
            raise HTTPException(400, detail = "Invalid startDateTime parameter")

    if (endDateTime := params.get("endDateTime")) != None:
        try:
            end = datetime.isoformat(endDateTime)
        except:
            raise HTTPException(400, detail = "Invalid endDateTime parameter")

    if (regionId := params.get("regionId")) != None:
        filters.append(f'regionId = {regionId}')

    if (weatherCondition := params.get("weatherCondition")) != None:
        if weatherCondition not in get_args(WEATHER_CONDITION):
            raise HTTPException(400, detail = "The weatherCondition is specified incorrectly")
        filters.append(f'weatherCondition = "{weatherCondition}"')

    skip  = params.get("form", 0)
    limit = params.get("size", 10)

    if skip < 0 or limit <= 0:
        raise HTTPException(400, detail = "Invalid \"form\" or/and \"size\" parameters")

    method = params.get("method", "AND")
    sql   += method.join(filters)

    database: aiosqlite.Connection = request.app.database

    cursor = await database.execute(sql)
    result = await cursor.fetchall()

    true_result = []

    if start or end:
        for climate in result:
            weather = await Weather.get_by(database, id = climate[0])

            if not weather:
                continue

            if start:
                if not weather.measurementDateTime > start:
                    continue

            if end:
                if not weather.measurementDateTime < end:
                    continue

            true_result.append(climate)
    else:
        true_result = result

    try:
        true_result = true_result[skip:limit]
    except:
        return JSONResponse([])

    return JSONResponse([{
        key: i[index] for index, key in enumerate(keys)
    } for i in true_result], status_code = 200)


@region_weather.post("/")
async def region_weather_add(request: Request):
    data: dict = await utils.parse.dictionary(request = request)

    if not data:
        raise HTTPException(400, detail = "Failed to get passed values, specify them in JSON format")

    if (data.get("regionId") == None) or (0 >= data.get("regionId")):
        raise HTTPException(400, detail = "Incorrect regionId")

    try:
        datetime.fromisoformat(data['measurementDateTime'])
    except:
        raise HTTPException(400, detail = "Incorrect dateTime")

    if data.get("weatherCondition") not in get_args(WEATHER_CONDITION):
        raise HTTPException(400, detail = "Incorrect weatherCondition")

    for key in ("windSpeed", "precipitationAmount"):
        if (data.get(key) == None) or (0 > data.get(key)):
            raise HTTPException(400, detail = f"Incorrect {key}")

    region = await Region.get_by(request.app.database, id = data["regionId"])

    if not region:
        raise HTTPException(404, detail = 'The region with this regionId was not found')

    for forecastId in data.get("WeatherForecast", []):
        forecast = await WeatherForecast.get_by(request.app.database, id = forecastId)

        if not forecast:
            raise HTTPException(404, detail = f'The region with ID {forecastId} was not found')

        if forecast.regionId != region.id:
            raise HTTPException(404, detail = f'The forecast with ID {forecastId} does not belong to the region')

    data["regionName"] = region

    weather = await Weather.create(request.app.database, **data)
    return JSONResponse(weather("id", "temperature", "humidity", "windSpeed", "weatherCondition", "precipitationAmount", "measurementDateTime", "weatherForecast"), status_code = 200)


@region_weather.put("/{regionld}")
async def region_weather_change(request: Request, regionld: int):
    if regionld == None or 0 >= regionld:
        raise HTTPException(400, detail = "Invalid ID")

    data = await utils.parse.dictionary(request)

    if not data:
        raise HTTPException(400, detail = "Failed to get passed values, specify them in JSON format")

    weather = await Weather.get_by(request.app.database, id = regionld)

    if not weather:
        raise HTTPException(404, detail = "The region type with this ID was not found")

    if data.get("weatherCondition") not in get_args(WEATHER_CONDITION):
        raise HTTPException(400, detail = "Incorrect weatherCondition")

    for key in ("windSpeed", "precipitationAmount"):
        if (data.get(key) == None) or (0 > data.get(key)):
            raise HTTPException(400, detail = f"Incorrect {key}")

    region = await Region.get_by(request.app.database, id = data["regionId"])

    if not region:
        raise HTTPException(404, detail = 'The region with this regionId was not found')

    for forecastId in data.get("WeatherForecast", []):
        forecast = await WeatherForecast.get_by(request.app.database, id = forecastId)

        if not forecast:
            raise HTTPException(404, detail = f'The region with ID {forecastId} was not found')

        if forecast.regionId != region.id:
            raise HTTPException(404, detail = f'The forecast with ID {forecastId} does not belong to the region')

    await weather.update(**data)
    return JSONResponse(weather("id", "regionId", "regionName", "temperature", "humidity", "windSpeed", "weatherCondition", "precipitationAmount", "measurementDateTime", "weatherForecast"), status_code = 200)


@region_weather.delete("/{regionld}")
async def region_weather_delete(request: Request, regionld: int):
    if regionld == None or 0 >= regionld:
        raise HTTPException(400, detail = "Invalid ID")

    weather = await Weather.get_by_id(regionld, request.app.database)

    if not weather:
        raise HTTPException(404, detail = "The region type with this ID was not found")

    await weather.delete()
    return Response(None)
